package org.zfes.snowier.common.monitor.http.okhttp3;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import org.zfes.snowier.common.monitor.http.okhttp3.handler.HandlerAynscHeader;
import org.zfes.snowier.common.monitor.http.okhttp3.handler.HandlerAynscJson;
import org.zfes.snowier.common.monitor.http.okhttp3.handler.HandlerAynscStream;
import org.zfes.snowier.common.monitor.http.okhttp3.handler.HandlerSyncHeader;
import org.zfes.snowier.common.monitor.http.okhttp3.handler.HandlerSyncJson;
import org.zfes.snowier.common.monitor.http.okhttp3.handler.HandlerSyncStream;
import org.zfes.snowier.common.monitor.http.response.HeaderResponse;
import org.zfes.snowier.common.monitor.http.response.JsonResponse;
import org.zfes.snowier.common.monitor.http.response.StreamResponse;
import com.google.common.collect.Lists;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class OkHttpManager {
	
	private static long connectionTimeout=10;
	private static long writeTimeout=20;
	private static long readTimeout=10;
	
	private OkHttpClient okHttpClient;
	private List<Interceptor> interceptors;
	
	private Map<String, String> params = null;
	private Map<String, String> heads = null;
	
	
    public static OkHttpManager newIns() {
    	OkHttpManager httpManager=new OkHttpManager();
        return httpManager;
    }
    public static OkHttpManager newIns(okhttp3.OkHttpClient.Builder okHttpClient) {
    	OkHttpManager httpManager=new OkHttpManager();
        return httpManager;
    }
    
    
	
	private OkHttpManager() {
	    	this.interceptors=Lists.newArrayList();
	        okHttpClient = new OkHttpClient();
	        okhttp3.OkHttpClient.Builder  builder=okHttpClient.newBuilder()
	        			 .connectTimeout(connectionTimeout, TimeUnit.SECONDS)
	        			 .writeTimeout(writeTimeout, TimeUnit.SECONDS)
	        			 .readTimeout(readTimeout, TimeUnit.SECONDS)
	        			 //.addInterceptor(new RetryIntercepter(1))
	        			 //.cache(new Cache(new File(System.getProperty("java.io.tmpdir")+"okhttpcache"), 1024 * 1024 * 10))
	        			 ;
	        if(!interceptors.isEmpty()) {
	        	interceptors.forEach(val-> builder.addInterceptor(val));
	        }
	       
	        this.params = new HashMap<String, String>();
	        this.heads=new HashMap<String, String>();
	     
	    }
	private OkHttpManager(OkHttpClient okHttpClient) {
    	this.interceptors=Lists.newArrayList();
        this.okHttpClient=okHttpClient;
        this.params = new HashMap<String, String>();
        this.heads=new HashMap<String, String>();
     
    }

	    public  void addInterceptor(Interceptor interceptor) {
	    	interceptors.add(interceptor);
	    }

	    
//=================================================================================================	    
	    public <R> Optional<R> getJson(String url, final HandlerSyncJson<R> handler) {
	    	return doRequest( url,  handler, RequestType.GET);
	    }
	    
	    public <R> void getAsyncJson(String url,HandlerAynscJson handler) {
	    	doAsyncRequest( url,  handler,RequestType.GET);
	    }
	    
	    public <R> Optional<R> getStream(String url,  HandlerSyncStream<R> handler) {
	    	return doRequestStream( url, handler, RequestType.GET);
	    }
	    
	    public <R> void getAsyncStream(String url,HandlerAynscStream handler) {
	    	doAsyncStream( url, handler,RequestType.GET) ;
	    }
	    

//=================================================================================================	 	    
	    public <R> Optional<R> post(String url, HandlerSyncJson<R> handler) {
	    	return doRequest( url,  handler, RequestType.POST);
	    }
	    
	    public <R> void postAsync(String url, HandlerAynscJson handler) {
	    	doAsyncRequest( url,  handler,RequestType.POST);
	    }
	    
	    
	    public <R> Optional<R> postStream(String url,  HandlerSyncStream<R> handler) {
	    	return doRequestStream( url, handler, RequestType.POST);
	    }
	    
	    public <R> void postAsyncStream(String url,HandlerAynscStream handler) {
	    	
	    	doAsyncStream( url, handler,RequestType.POST) ;
	       
	    }
		
//=================================================================================================	    
	    public <R> Optional<R> head(String url, final HandlerSyncHeader<Response, ? extends R> handler) {
	    	 Response response =null;
	    	try {
				  response = okHttpClient.newCall( buildRequest(url, RequestType.HEAD)).execute();
				 if(!response.isSuccessful()) {
					 response.close();
				 }
				 return Optional.of(handler.onResponse(new HeaderResponse(response.headers().toMultimap(), response.code())));
			} catch (IOException e) {
				return Optional.of(handler.onResponse(new HeaderResponse(500).setError(e.getMessage())));
			}
	    }
	    
	    
	    public <R> void headAsync(String url,HandlerAynscHeader handler) {
	        okHttpClient.newCall(buildRequest(url, RequestType.GET))
				.enqueue(new Callback() {
			            @Override
			            public void onFailure(Call call, IOException e) {
			            	handler.onResponse(new HeaderResponse(500).setError(e.getMessage()));
			            }
			            @Override
			            public void onResponse(Call call, Response response) {
			            	 handler.onResponse(new HeaderResponse(response.headers().toMultimap(), response.code()));
			            }
				});
	    } 
//=================================================================================================	 		  	   


	    
	
	public <R> Optional<R> doRequestStream(String url,  HandlerSyncStream<R> handler,RequestType type) {
		try {
			 Response response = okHttpClient.newCall(buildRequest(url,  type)).execute();
			 if(response.isSuccessful()) {
				 return Optional.of(handler.onResponse(new StreamResponse(response.body().byteStream(), response.code())));
			 }else {
				 return Optional.of(handler.onResponse(new StreamResponse( response.code())));
			 }
			 
		} catch (IOException e) {
			return	Optional.of(handler.onResponse(new StreamResponse(500).setError(e.getMessage())));
		}
	}
	
	  public <R> void postAsyncFile(String url,String filePath,String fileParamName,HandlerAynscJson handler) {
	        
	        Call call = okHttpClient.newCall(buildMultipartRequest( url, filePath,  fileParamName) );
	        call.enqueue(new Callback() {
	            @Override
	            public void onFailure(Call call, IOException e) {
	            	handler.onResponse(new JsonResponse(500).setError(e.getMessage()));
	            }

	            @Override
	            public void onResponse(Call call, Response response) {
	            	try {
	            		handler.onResponse(new JsonResponse(response.body().string(), response.code()));
					} catch (IOException e) {
						e.printStackTrace();
						handler.onResponse(new JsonResponse(500).setError(e.getMessage()));
					}
	            }
		});
		}

	private  <R> void doAsyncStream(String url,HandlerAynscStream handler,RequestType type) {
		  
		   okHttpClient.newCall(buildRequest(url, type))
			.enqueue(new Callback() {
		            @Override
		            public void onFailure(Call call, IOException e) {
		            	handler.onResponse(new StreamResponse(500).setError(e.getMessage()));
		            }
		            @Override
		            public void onResponse(Call call, Response response) {
		            	 if(response.isSuccessful()) {
		            		 handler.onResponse(new StreamResponse(response.body().byteStream(), response.code()));
						 }else {
							 handler.onResponse(new StreamResponse(response.code()));
							 response.close();
						 }
		            	
		            }
			});
	}
	 
//=================================================================================================	 



		private <R> Optional<R> doRequest(String url,  HandlerSyncJson<R> handler,RequestType type){
			Response response =null;
			try {
				  response = okHttpClient.newCall(buildRequest(url, type)).execute();
				 
				 return Optional.of(handler.onResponse(new JsonResponse(response.body().string(), response.code())));
				 
			} catch (IOException e) {
				e.printStackTrace();
				 response.close();
				return  Optional.of(handler.onResponse(new JsonResponse(500).setError(e.getMessage())));
			}
	   } 
		
	    private  void  doAsyncRequest(String url, HandlerAynscJson handler,RequestType type){
	    	 okHttpClient.newCall(buildRequest(url, type))
				.enqueue(new Callback() {
			            @Override
			            public void onFailure(Call call, IOException e) {
			            	handler.onResponse(new JsonResponse(500).setError(e.getMessage()));
			            }
		
			            @Override
			            public void onResponse(Call call, Response response) {
			            	try {
			            		handler.onResponse(new JsonResponse(response.body().string(), response.code()));
							} catch (IOException e) {
								e.printStackTrace();
								handler.onResponse(new JsonResponse(500).setError(e.getMessage()));
							}
			            }
				});
	    }
	    
	  
	    
	
	    private Request buildRequest(String url, RequestType type) {
	        Request.Builder builder = new Request.Builder();
	        
	        switch (type) {
			case GET:
				url = getParamWithString(url);
	            builder.get();
				break;
			case POST:
				 RequestBody requestBody = getFormatData(params);
		         builder.post(requestBody);
				break;
			case HEAD:
				url = getParamWithString(url);
	            builder.head();
				break;
			case DELETE:
				url = getParamWithString(url);
	            builder.delete();
				break;
			default:
				url = getParamWithString(url);
	            builder.get();
				break;
			}
	        builder.url(url);
	        addAllHeads(builder);
	        return builder.build();
	    }
	
	    
	    private RequestBody getFormatData(Map<String, String> params) {
	        FormBody.Builder builder = new FormBody.Builder();
	        if (params != null && params.size() > 0) {
	        	 params.forEach((key,val)-> builder.add(key, val));
	        }
	        return builder.build();
	    }
	    
	    private void addAllHeads(Request.Builder builder) {
	    	heads.forEach((key,val)-> builder.addHeader(key,val));
	    }


	    public String getParamWithString(String url) {
	        if (params == null || params.size() < 1)
	            return url;
	        
	        StringBuilder requestUrl = new StringBuilder();
	        
	        if (url.indexOf("http://") == 0 || url.indexOf("https://") == 0) {
	        	requestUrl.append(url + "?");
	        }
	        params.forEach((key,val)->requestUrl.append(key).append("=").append(val).append("&"));

	        return requestUrl.toString().substring(0, (requestUrl.toString().length() - 1));
	    }

	
	    public Map<String, String> getParams() {
	        return params;
	    }


	    public OkHttpManager addParam(String key, String value) {
	        this.params.put(new String(key), value);
	        return this;
	    }

	    public Map<String, String> getHeads() {
	        return heads;
	    }

	    public OkHttpManager addHead(String key, String value) {
	        this.heads.put(key, value);
	        return this;
	    }
		

	
	    private Request buildMultipartRequest(String url,String filePath, String fileParamName) {
	        Request.Builder builder = new Request.Builder();
	        RequestBody requestBody =  getMultipartFormatData(params, filePath,fileParamName) ;
	        builder.post(requestBody);
	        builder.url(url);
	        addAllHeads(builder);
	        return builder.build();
	    }
	    private RequestBody getMultipartFormatData(Map<String, String> params, String filePath, String fileParamName) {
	    		File file = new File(filePath);

		        RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
		     
		        okhttp3.MultipartBody.Builder builder = new MultipartBody.Builder()
		                .setType(MultipartBody.FORM)
		                .addFormDataPart(fileParamName, file.getName(), fileBody);
		        
		        if (params != null && params.size() > 0) {
		        	 params.forEach((key,val)-> builder.addFormDataPart(key, val));
		        }
	        return builder.build();
	    }

		
}
